package com.kaibes.core.base.object;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ObjectContext {

    private static ObjectContext instance;

    private Map<String, ObjectPlan> namePlanMap = new HashMap<>();
    private Map<String, ObjectPlan> typePlanMap = new HashMap<>();

    public static void run(Class<?> clazz) {
        run(clazz.getPackage().getName());

    }

    public static void run(String packgeName) {
        instance = new ObjectContext();
        List<Class<?>> classList = ClassParser.getMXClass(packgeName);
        for (Class<?> clazzC : classList) {
            try {
                Object obj = clazzC.getDeclaredConstructor().newInstance();
                List<Method> methods = ClassParser.getMXMethod(clazzC);
                for (Method method : methods) {
                    instance.addPlan(obj, method);
                }
            } catch (InstantiationException|IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
                e.printStackTrace();
            }
        }

    }

    @SuppressWarnings("unchecked")
    public static <T> T getValue(String name) {
        ObjectPlan plan = instance.namePlanMap.get(name);
        if (plan != null) {
            return (T) plan.getValue();
        }
        return null;
    }

    public static void clearValue(String name) {
        if (instance.namePlanMap.containsKey(name)) {
            instance.namePlanMap.get(name).value = null;
        }
    }

    public static Object getValue(Class<?> type) {
        ObjectPlan plan = instance.typePlanMap.get(type.getName());
        if (plan == null) {
            plan = instance.addPlan(type);
        }
        return plan.value;
    }

    public static void clearValue(Class<?> clazz) {
        if (instance.typePlanMap.containsKey(clazz.getName())) {
            instance.typePlanMap.remove(clazz.getName());
        }
    }

    public static void initValue(Object obj) {
        List<Field> fields = ClassParser.getMXField(obj.getClass());
        for (Field field : fields) {
            BesResource resource = field.getDeclaredAnnotation(BesResource.class);
            String name = field.getName();
            if (!resource.value().isEmpty()) {
                name = resource.value();
            }

            try {
                field.setAccessible(true);
                Object value = getValue(name);
                if (value == null) {
                    value = getValue(field.getType());
                }
                field.set(obj, value);
            } catch (IllegalArgumentException|IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private ObjectPlan addPlan(Class<?> type) {
        ObjectPlan plan = null;
        try {
            Object value = type.getDeclaredConstructor().newInstance();
            plan = new ObjectPlan();
            plan.value = value;
            typePlanMap.put(type.getName(), plan);
        } catch (InstantiationException|IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
            e.printStackTrace();
        }
        return plan;
    }

    private void addPlan(Object obj, Method method) {
        ObjectPlan plan = new ObjectPlan();
        plan.obj = obj;
        plan.createMethod = method;

        BesResource resource = method.getDeclaredAnnotation(BesResource.class);
        String name = method.getName();
        if (!resource.value().isEmpty()) {
            name = resource.value();
        }
        namePlanMap.put(name, plan);
    }

    private class ObjectPlan {
        private Object obj;
        private Method createMethod;
        private Object value;

        private Object getValue() {
            if (value == null) {
                try {
                    Parameter[] ps = createMethod.getParameters();
                    Object[] objs = new Object[ps.length];
                    for (int i = 0; i < ps.length; i++) {
                        Parameter parameter = ps[i];
                        String name = parameter.getName();
                        if (parameter.isAnnotationPresent(BesResource.class)) {
                            BesResource resource = parameter.getDeclaredAnnotation(BesResource.class);
                            String value = resource.value();
                            if (!value.isEmpty()) {
                                name = value;
                            }
                        }
                        Object obj = ObjectContext.getValue(name);
                        if (obj == null) {
                            obj = ObjectContext.getValue(parameter.getType());
                        }
                        objs[i] = obj;
                    }
                    value = createMethod.invoke(obj, objs);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
            return value;
        }
    }
}
